package com.ssyx.activity.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ssyx.activity.feign.ProductFeignClient;
import com.ssyx.activity.mapper.CouponInfoMapper;
import com.ssyx.activity.mapper.CouponRangeMapper;
import com.ssyx.activity.mapper.CouponUseMapper;
import com.ssyx.activity.service.CouponInfoService;
import com.ssyx.enums.CouponRangeType;
import com.ssyx.enums.CouponStatus;
import com.ssyx.enums.CouponType;
import com.ssyx.model.activity.CouponInfo;
import com.ssyx.model.activity.CouponRange;
import com.ssyx.model.activity.CouponUse;
import com.ssyx.model.order.CartInfo;
import com.ssyx.model.product.Category;
import com.ssyx.model.product.SkuInfo;
import com.ssyx.vo.activity.CouponRuleVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName:CouponInfoServiceImpl
 * Description:
 *
 * @Author kay
 * @Create 2024/5/5 23:27
 * @Version 1.0
 */
@Service
public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponInfo>
                    implements CouponInfoService {

    @Autowired
    private CouponRangeMapper couponRangeMapper;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private CouponUseMapper couponUseMapper;

    @Override
    public IPage<CouponInfo> getPage(Page<CouponInfo> couponParams) {
        IPage<CouponInfo> couponInfoPage = baseMapper.selectPage(couponParams, null);
        List<CouponInfo> couponInfoList = couponInfoPage.getRecords();
        couponInfoList.stream().forEach(item ->{
            //设置优惠券类型
            item.setCouponTypeString(item.getCouponType().getComment());
            //设置优惠券的使用范围
            CouponRangeType rangeType = item.getRangeType();
            if(rangeType != null){
                item.setRangeTypeString(rangeType.getComment());
            }
        });
        return couponInfoPage;
    }

    //获取优惠券
    @Override
    public CouponInfo getCouponInfoById(Long id) {
        CouponInfo couponInfo = baseMapper.selectById(id);
        couponInfo.setCouponTypeString(couponInfo.getCouponType().getComment());
        CouponRangeType rangeType = couponInfo.getRangeType();
        if(rangeType != null){
            couponInfo.setRangeTypeString(rangeType.getComment());
        }
        return couponInfo;
    }

    //获取优惠券规则信息
    @Override
    public Map<String, Object> findCouponRuleList(Long id) {
        Map<String, Object> map = new HashMap<>();
        //前端传过来的id为couponId
        //根据couponId查询表coupon_range表,得到couponRangeList
        List<CouponRange> couponRangeList = couponRangeMapper.selectList(new LambdaQueryWrapper<CouponRange>()
                .eq(CouponRange::getCouponId, id));

        //根据couponRangeList得到rangeIdList
        List<Long> rangeIdList = couponRangeList.stream()
                .map(CouponRange::getRangeId)
                .collect(Collectors.toList());

        //根据couponId,查询coupon_info表,得到rangeType
        //rangeType 分三种:商品sku,分类Category,通用
        CouponInfo couponInfo = baseMapper.selectById(id);
        CouponRangeType rangeType = couponInfo.getRangeType();
        if(!CollectionUtils.isEmpty(rangeIdList)){
            //rangeType为sku时,rangeIdList为商品skuId的集合
            if(rangeType == CouponRangeType.SKU){
                //远程调用,得到商品skuInfo集合,并封装返回
                List<SkuInfo> skuInfoList = productFeignClient.findSkuInfoList(rangeIdList);
                map.put("skuInfoList",skuInfoList);
            //rangeType为category时,rangeIdList为商品categoryId的集合
            }else if(rangeType == CouponRangeType.CATEGORY){
                //远程调用,得到商品category集合,并封装返回
                List<Category> categoryList = productFeignClient.getCategoryList(rangeIdList);
                map.put("categoryList",categoryList);
            }else{
                //通用
            }
        }
        return map;
    }

    //添加优惠券规则数据
    @Override
    public void saveCouponRule(CouponRuleVo couponRuleVo) {
        //根据优惠券id删除规则数据
        Long couponId = couponRuleVo.getCouponId();
        couponRangeMapper.delete(new LambdaQueryWrapper<CouponRange>().eq(CouponRange::getCouponId,couponId));

        //更新优惠券基本信息,即表coupon_info
        //优惠券分为现金券和满减券
        //现金券,属性ConditionAmount默认为0,属性amount为金额
        //满减券,满金额为属性ConditionAmount,减金额为属性amount
        //所以直接更新ConditionAmount和amount,即可
        CouponInfo couponInfo = baseMapper.selectById(couponId);
        couponInfo.setRangeType(couponRuleVo.getRangeType());
        couponInfo.setAmount(couponRuleVo.getAmount());
        couponInfo.setConditionAmount(couponRuleVo.getConditionAmount());
        couponInfo.setRangeDesc(couponRuleVo.getRangeDesc());
        baseMapper.updateById(couponInfo);

        //添加优惠券新规则数据,即表coupon_range
        List<CouponRange> couponRangeList = couponRuleVo.getCouponRangeList();
        for(CouponRange couponRange : couponRangeList){
            couponRange.setCouponId(couponId);
            couponRangeMapper.insert(couponRange);
        }
    }

    //购物车可使用的优惠券列表
    @Override
    public List<CouponInfo> findCartCouponInfo(List<CartInfo> cartInfoList, Long userId) {
        //获取全部用户优惠券(coupon_use,coupon_info)
        List<CouponInfo> userAllCouponInfoList = baseMapper.selectCartCouponInfoList(userId);

        //如果该用户没有优惠券，则返回一个空集合
        if(CollectionUtils.isEmpty(userAllCouponInfoList)) {
            return new ArrayList<CouponInfo>();
        }

        //获取优惠券id列表
        List<Long> couponIdList = userAllCouponInfoList.stream().
                map(couponInfo -> couponInfo.getId())
                .collect(Collectors.toList());

        //查询优惠券对应的范围集合
        LambdaQueryWrapper<CouponRange> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CouponRange::getCouponId, couponIdList);
        List<CouponRange> couponRangesList = couponRangeMapper.selectList(wrapper);

        //获取优惠券id对应的满足使用范围的购物项skuId列表,Map<Long, List<Long>> key:CouponId,value:skuIdList 券->商品
        Map<Long, List<Long>> couponIdToSkuIdMap = this.findCouponIdToSkuIdMap(cartInfoList, couponRangesList);
        //计算优惠后减少金额
        BigDecimal reduceAmount = new BigDecimal("0");
            //记录最优优惠券
        CouponInfo optimalCouponInfo = null;
        //遍历所有优惠券信息列表
        for(CouponInfo couponInfo : userAllCouponInfoList) {
            //如果优惠券为通用优惠券
            if(CouponRangeType.ALL == couponInfo.getRangeType()) {
                //计算购物车商品的总价
                BigDecimal totalAmount = computeTotalAmount(cartInfoList);
                //总金额 减去 优惠金额 >=0 ,则该优惠券生效
                if(totalAmount.subtract(couponInfo.getConditionAmount()).doubleValue() >= 0){
                    couponInfo.setIsSelect(1);
                }
            }else{
                //优惠券为指定商品 或 指定分类时 ,商品项信息记录在couponIdToSkuIdMap中,见方法findCouponIdToSkuIdMap()
                Long couponId = couponInfo.getId();
                //根据优惠券id,在couponIdToSkuIdMap中,得到可将skuId集合
                List<Long> skuIdList = couponIdToSkuIdMap.get(couponId);
                //当前满足优惠券使用范围的商品项
                List<CartInfo> currentCartInfoList = cartInfoList.stream().
                        filter(cartInfo -> skuIdList.contains(cartInfo.getSkuId()))
                        .collect(Collectors.toList());
                //计算购物车商品的总价
                BigDecimal totalAmount = computeTotalAmount(currentCartInfoList);
                //总金额 减去 优惠金额 >=0 ,则该优惠券生效
                if(totalAmount.subtract(couponInfo.getConditionAmount()).doubleValue() >= 0){
                    couponInfo.setIsSelect(1);
                }
            }
            //如果优惠券生效,且优惠券的金额>优惠后减少的金额,则
            //reduceAmount 为 前面设置的优惠后减少的金额,optimalCouponInfo为前面设置的最优的优惠券信息
            if (couponInfo.getIsSelect().intValue() == 1 &&
                    couponInfo.getAmount().subtract(reduceAmount).doubleValue() > 0) {
                reduceAmount = couponInfo.getAmount();
                optimalCouponInfo = couponInfo;
            }
        }
        //如果能找到最优优惠券信息,则设置该优惠券为有效
        if(null != optimalCouponInfo) {
            optimalCouponInfo.setIsOptimal(1);
        }
        return userAllCouponInfoList;
    }


    //购物车中商品项列表,计算所有商品项的总价格
    private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
        BigDecimal total = new BigDecimal("0");
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if(cartInfo.getIsChecked().intValue() == 1) {
                BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                total = total.add(itemTotal);
            }
        }
        return total;
    }

    //获取优惠券id对应的满足使用范围的商品项skuId列表
    //说明：一个优惠券可能有多个商品项满足它的使用范围，那么多个商品项可以拼单使用这个优惠券
    private Map<Long, List<Long>> findCouponIdToSkuIdMap(List<CartInfo> cartInfoList,
                                                         List<CouponRange> couponRangesList) {
        Map<Long, List<Long>> couponIdToSkuIdMap = new HashMap<>();

        //优惠券id对应的范围列表 Map<Long, List<CouponRange>> key:优惠券id,value:该优惠券id对应的范围列表
        Map<Long, List<CouponRange>> couponIdToCouponRangeListMap = couponRangesList.stream()
                .collect(Collectors.groupingBy(couponRange -> couponRange.getCouponId()));
        //遍历上述Map
        Iterator<Map.Entry<Long, List<CouponRange>>> iterator = couponIdToCouponRangeListMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, List<CouponRange>> entry = iterator.next();
            //key:优惠券id
            Long couponId = entry.getKey();
            //value:该优惠券id对应的范围列表
            List<CouponRange> couponRangeList = entry.getValue();

            //新建一个set,存放满足优惠券条件的skuId的集合
            Set<Long> skuIdSet = new HashSet<>();
            //遍历购物车商品项 和 优惠券id对应的范围列表
            for (CartInfo cartInfo : cartInfoList) {
                for(CouponRange couponRange : couponRangeList) {
                    //如果优惠券使用范围是指定商品,且商品项中的商品与指定商品是同一商品,则将商品skuId加入集合
                    if(CouponRangeType.SKU == couponRange.getRangeType() &&
                            couponRange.getRangeId().longValue() == cartInfo.getSkuId().intValue()) {
                        skuIdSet.add(cartInfo.getSkuId());
                    //如果优惠券使用范围是指定分类,且商品项中的商品的分类为该指定分类,则将商品skuId加入集合
                    } else if(CouponRangeType.CATEGORY == couponRange.getRangeType() &&
                            couponRange.getRangeId().longValue() == cartInfo.getCategoryId().intValue()) {
                        skuIdSet.add(cartInfo.getSkuId());
                    } else {
                    //通用券时,不做操作
                    }
                }
            }
            //couponIdToSkuIdMap  优惠券id对应的满足使用范围的商品项skuId列表
            couponIdToSkuIdMap.put(couponId,new ArrayList<>(skuIdSet));
        }

        return couponIdToSkuIdMap;
    }

    //根据购物车列表,以及用户的优惠券id,得到购物车商品使用优惠券的相关规则信息
    //CouponInfo类中有一字段List<skuId>,从购物车列表中获取满足优惠券活动范围的商品skuId的集合
    @Override
    public CouponInfo findRangeSkuIdList(List<CartInfo> cartInfoList, Long couponId) {

        //根据优惠券id,查询优惠券基本信息
        CouponInfo couponInfo = baseMapper.selectById(couponId);
        if(couponInfo==null){
            return null;
        }
        LambdaQueryWrapper<CouponRange> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CouponRange::getCouponId,couponId);
        List<CouponRange> couponRangeList = couponRangeMapper.selectList(wrapper);
        //根据购物车商品项列表,相关优惠券规则范围,获取优惠券id对应的满足使用范围的商品项skuId列表
        Map<Long, List<Long>> couponIdToSkuIdMap = findCouponIdToSkuIdMap(cartInfoList, couponRangeList);
        List<Long> skuIdList = couponIdToSkuIdMap.get(couponId);
        couponInfo.setSkuIdList(skuIdList);
        return couponInfo;
    }

    //订单生成之后,修改优惠券使用状态
    @Override
    public void updateCouponInfoUseStatus(Long couponId,Long orderId,Long userId) {
        CouponUse couponUse = new CouponUse();
        couponUse.setOrderId(orderId);
        couponUse.setCouponStatus(CouponStatus.USED);
        couponUse.setUsingTime(new Date());

        QueryWrapper<CouponUse> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("coupon_id", couponId);
        queryWrapper.eq("user_id", userId);
        couponUseMapper.update(couponUse, queryWrapper);
    }
}
